#ifndef DW_GRAFFITI_GEOMETRY_BUILDER_H
#	define DW_GRAFFITI_GEOMETRY_BUILDER_H

#include "declaration.h"

namespace dw {
	namespace graffiti {

struct GeometryBuilder
{
	template< typename IndexT >
	static void buildFilledCube(const AttributeDeclaration* attributeDeclarations, dw::uint32 attributesCount,
		void*& vertexData, dw::uint32& vertexCount, IndexT*& indexData, dw::uint32& indexCount)
	{
		bool hasPosition = false;
		bool hasNormal = false;
		bool hasColor = false;

		dw::uint32 positionOffset = 0;
		dw::uint32 normalOffset = 0;
		dw::uint32 colorOffset = 0;
		dw::uint32 vertexSize = 0;

		for (dw::uint32 attributeIt = 0; attributeIt < attributesCount; ++attributeIt)
		{
			const AttributeDeclaration& attribute = attributeDeclarations[attributeIt];

			dwAssert(attribute.semanticIndex == 0, "");
			dwAssert(attribute.type == attribute::type::vec3, "");

			switch (attribute.semantic)
			{
			case attribute::semantic::position :
				hasPosition = true;
				positionOffset = vertexSize;
				break ;
			case attribute::semantic::normal :
				hasNormal = true;
				normalOffset = vertexSize;
				break ;
			case attribute::semantic::color :
				hasColor = true;
				colorOffset = vertexSize;
				break ;
			}

			vertexSize += attribute::type::size(attribute.type);
		}

		dwAssert(hasPosition, "");

		indexCount = cubeIndicesCount;
		indexData = new IndexT [indexCount];

		if (hasNormal)
		{
			vertexCount = 6 * 4;
			dw::byte* data = new dw::byte [vertexSize * vertexCount];

			for (dw::uint32 faceIt = 0; faceIt < 6; ++faceIt)
			{
				IndexT* faceIndex = &indexData[faceIt * 2 * 3];

				for (dw::uint32 indexIt = 0; indexIt < 6; ++indexIt)
				{
					faceIndex[indexIt] = cubeIndices[indexIt] + faceIt * 4;

					dw::byte* vertexData = &data[faceIndex[indexIt] * vertexSize];

					if (hasPosition)
					{
						glm::vec3* pos = (glm::vec3*)(&vertexData[positionOffset]);
						*pos = cubeVertices[cubeIndices[faceIt * 2 * 3 + indexIt] * 2];
					}
					if (hasColor)
					{
						glm::vec3* color = (glm::vec3*)(&vertexData[colorOffset]);
						*color = cubeVertices[cubeIndices[faceIt * 2 * 3 + indexIt] * 2 + 1];
					}

					glm::vec3* normal = (glm::vec3*)(&vertexData[normalOffset]);
					*normal = cubeNormals[faceIt];
				}
			}

			vertexData = data;
		}
		else
		{
			for (dw::uint32 indexIt = 0; indexIt < indexCount; ++indexIt)
			{
				indexData[indexIt] = cubeIndices[indexIt];
			}

			vertexCount = 8;
			dw::byte* data = new dw::byte [vertexSize * vertexCount];

			for (dw::uint32 vertexIt = 0; vertexIt < vertexCount; ++vertexIt)
			{
				dw::byte* vertexData = &data[vertexIt * vertexSize];

				if (hasPosition)
				{
					glm::vec3* pos = (glm::vec3*)(&vertexData[positionOffset]);
					*pos = cubeVertices[vertexIt * 2];
				}
				if (hasColor)
				{
					glm::vec3* color = (glm::vec3*)(&vertexData[colorOffset]);
					*color = cubeVertices[vertexIt * 2 + 1];
				}
			}

			vertexData = data;
		}
	}

	template< typename IndexT >
	static void buildFilledIcosahedron(const AttributeDeclaration* attributeDeclarations, dw::uint32 attributesCount,
		void*& vertexData, dw::uint32& vertexCount, IndexT*& indexData, dw::uint32& indexCount)
	{
		bool hasPosition = false;
		bool hasNormal = false;
		bool hasColor = false;

		dw::uint32 positionOffset = 0;
		dw::uint32 normalOffset = 0;
		dw::uint32 colorOffset = 0;
		dw::uint32 vertexSize = 0;

		for (dw::uint32 attributeIt = 0; attributeIt < attributesCount; ++attributeIt)
		{
			const AttributeDeclaration& attribute = attributeDeclarations[attributeIt];

			dwAssert(attribute.semanticIndex == 0, "");
			dwAssert(attribute.type == attribute::type::vec3, "");

			switch (attribute.semantic)
			{
			case attribute::semantic::position :
				hasPosition = true;
				positionOffset = vertexSize;
				break ;
			case attribute::semantic::normal :
				hasNormal = true;
				normalOffset = vertexSize;
				break ;
			case attribute::semantic::color :
				hasColor = true;
				colorOffset = vertexSize;
				break ;
			}

			vertexSize += attribute::type::size(attribute.type);
		}

		dwAssert(hasPosition, "");

		indexCount = icoIndicesCount;
		indexData = new IndexT [indexCount];

		for (dw::uint32 indexIt = 0; indexIt < indexCount; ++indexIt)
		{
			indexData[indexIt] = icoIndices[indexIt];
		}

		vertexCount = icoVerticesCount;
		dw::byte* data = new dw::byte [vertexSize * vertexCount];
		
		for (dw::uint32 vertexIt = 0; vertexIt < vertexCount; ++vertexIt)
		{
			dw::byte* vertexData = &data[vertexIt * vertexSize];

			if (hasPosition)
			{
				glm::vec3* pos = (glm::vec3*)(&vertexData[positionOffset]);
				*pos = glm::normalize(icoVertices[vertexIt]);
			}
			if (hasColor)
			{
				glm::vec3* color = (glm::vec3*)(&vertexData[colorOffset]);
				*color = glm::normalize(0.5f * icoVertices[vertexIt] + glm::vec3(0.5f));
			}
			if (hasNormal)
			{
				glm::vec3* normal = (glm::vec3*)(&vertexData[normalOffset]);
				*normal = glm::normalize(icoVertices[vertexIt]);
			}
		}

		vertexData = data;
	}

private : //! @name Constants
	//! @{
	static const dw::uint32 cubeIndicesCount = 6 * 2 * 3;
	static const dw::byte cubeIndices[cubeIndicesCount];
	static const dw::uint32 cubeVerticesCount = 8;
	static const glm::vec3 cubeVertices[cubeVerticesCount * 2]; // position & color
	static const glm::vec3 cubeNormals[6];
	
	static const dw::uint32 icoIndicesCount = 20 * 3;
	static const dw::byte icoIndices[icoIndicesCount];
	static const dw::uint32 icoVerticesCount = 12;
	static const glm::vec3 icoVertices[icoVerticesCount]; // position == normal == color
	//! @}
};

	} // namespace graffiti
} // namespace dw

#endif // !DW_GRAFFITI_GEOMETRY_BUILDER_H
